home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-09-02 | 59.7 KB | 1,489 lines | [TEXT/CCL2] |
- (in-package menus)
-
- ;;marking-menu.lisp
- ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; marking-menu.lisp
- ;;
- ;; Copyright © 1992 University of Toronto, Department of Computer Science
- ;; All Rights Reserved
- ;;
- ;; author: Mark A. Tapia
- ;;
- ;; Methods to support a new mixin class of menus for views: marking-menus.
- ;;
- ;; Marking menus support hierarchical menus. Each menu item in a
- ;; marking menu must be a menu-item, a window-menu-item, or a
- ;; marking-menu-view.
- ;;
- ;;
- ;; See also:
- ;; About-marking-menus which describes the underlying concepts
- ;; Marking-menu-demo.lisp which contains a full demonstration of marking menus
- ;; Hier-menu-demo.lisp which contains a full demonstration of hierarchical
- ;; marking menus
- ;;
- ;; Change history
- ;; 1992-05-13 support for automatically sizing menus (:auto-size)
- ;; support of menus with color screens, even when the
- ;; window containing the marking-menu straddles screens
- ;; 1992-05-22 compatability features added for MCL2.0f...
- ;;
- ;; Future enhancements
- ;; support for color menu items within marking-menus
- ;; marking ahead for hierarchical (multi-level) menus
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- (provide :marking-menus)
-
- (eval-when (eval compile)
- (require 'quickdraw)
- (require 'oou-utils))
-
- #+mcl-final
- (eval-when (eval compile)
- ;; all record definitions are now autoloaded in MCL2.0f
- (require 'records)
- ;; loop.lisp is automatically loaded when required in MCL2.0f...
- (require :loop))
-
- (export '(marking-menu empty-menu-item marking-menu-table marking-menu-view marking-menu-window
- containing-view
- resize-menu
- menu-root)
- :menus)
- #|
- This mixin extends menus to include marking menus.
-
- For details on the underlying concepts see about-marking-menus.
-
- Marking-menu ; a subclass of menus, not exported
- Initargs (in addition to the standard menu initargs)
-
- :menu-diameter ; default pop-menu-diameter (170)
- diameter of the circular pop-up marking menu
-
- :auto-size ; default t
- automatically compute the diameter of the marking-menu to allow
- enough white space.
-
- :offset ; default pop-menu-height (2)
- offset of the black circle underneath
-
- :hole-size ; default hole-size (10)
- diameter of the dead zone. Releasing the mouse button in this circle
- selects nothing from the menu
-
- :menu-start-tol ; default jitter-tol (5)
- mouse is "still" when menu-start-tol is greater than the sum of the
- horizontal and vertical pixel distance of the current mouse position from
- the mouse position at the start of the time interval.
-
- :menu-font ; default '("Chicago" 12 :SRCOR :PLAIN)
- font spec for the menu items
-
- :on-axis ; default nil
- does first menu slice begin at North? Default is no. A straight up motion
- selects the first item when not on-axis
-
- :pop-up-time ; default wait 1/12 second
- elapsed time until menu pops up when the mouse remains at most
- menu-start-tol distance from the initial position (vertical + horizontal)
- units are in internal-time-units-per-second.
-
- :menu-double-click-action ; default none, no function invoked
- A function to be called when the item is double clicked. The function
- should accept one argument, the item (normally a marking-view). The
- action run is the one associated with the most specific marking-menu-view
-
- :menu-actzone ; default nil
- Menu will be active when mouse button is pressed in this rectangle
- (local coordinates, of the form
- (topLeft bottomRight) - the rectangle defined by the two points
-
- :menu-opaque ; default nil
- Valid only with floating menus.
- When true, greys out the outer ring of a floating marking menu,
- revealing the context of the menu.
- Otherwise, whites-out the outer ring of the marking menu, obscuring
- the context.
-
- :menu-floating ; default t
- Creates a menu which appears to be floating over the view.
- Lines connect the center of the menu item titles to the central hub.
- Otherwise, lines divide the menu so that the title appear in the middle
- of each wedge.
-
- :hide ; default t
- hide the other menu items when a menu item corresponding to a menu
- is selected.
-
- :in-position ; default nil
- when hide and menu-floating are in effect, places the menu item
- to the left of the central spoke and removes the box
- when in-position is true; otherwise leaves the box around the menu
- item.
-
- :turn ; default t
- when turn is in effect, the menus alternate between on and off axis.
- Otherwise, all menus are either on/off axis. For sub menus, the on-axis
- specification is ignored.
-
- :pop-width ; default 1/3 * menu-radius
- A hierarchical menu pops up when the mouse is still and the current
- point is more than pop-width pixels from the menu-center.
-
- Note: sub-menus inherit the following attributes of the root (parent) menu:
- menu-floating, opaque, hide, in-position turn
-
- Marking-menu-view ; a marking-menu view, exported
- :initarg same as marking-menu and view
-
-
- Marking-menu-window ; a marking-view window, exported
- :initarg same as marking-menu and window
-
- Empty-menu-item ; a menu-item, exported
- Use instances of this item to create empty menu items. The item
- will be disabled and will have a title of "-".
-
- Methods of interest
- Containing view returns the view associated with a menu or the
- view associated with a menu of the which the menu-item is a part.
-
- (Containing-view marking-menu)
- Returns the view associated with the menu.
- For a hier-marking-menu, the result is the root of the menu tree.
- For all other marking-menus, the result is the marking-menu itself.
-
- (Containing-view menu-item)
- When the menu-item is associated with a marking-menu, the result
- is the containing-view of the marking-menu.
-
- Otherwise, the result is the menu associated with the menu-item.
-
- Menu-double-click-action
- Peforms the action (if any) associated with double-clicking in the
- view.
-
- (menu-double-click-action marking-menu)
- When a double-click-function is defined for the markking menu,
- performs the function.
-
- Do-menu-item-action
- Do-menu-item-action invokes the menu-item-action-function associated
- with the menu item with the appropriate paramters. Allows the
- action to be performed with normal menus as well.
-
- Note: For functions which change the contents of any view, use
- "eval-enqueue" to ensure that the hierarchical menu is erased
- properly before the action is performed.
-
- (do-menu-item-action menu-item)
- Invokes the menu-item-action-function associated with the menu-item
- with no parameters.
-
- (do-menu-item-action window-menu-item)
- Invokes the menu-item-action-function associated with the menu-item
- with one parameter - the window-menu-item.
-
- (do-menu-item-action marking-menu)
- Has no effect, specialize to perform other actions after selecting
- but not invoking a hier-menu.
-
- Resize-menu
- (Resize-menu marking-menu-view)
- For all marking-menu-views, adjusts the containing menu-rect to
- include all of the menu-titles.
-
- For auto-size marking-menu-views, adjust the menu-radius and
- menu-diameter of the marking-value to allow enough white space
- between each menu item and the inner circle and the outer circle.
-
- Set-menu-font
- (set-menu-font marking-menu-view font-spect)
- Sets the font-spec to be used for the marking menu. Resizes the menu
- automatically.
-
- menu-root
- (menu-root marking-menu-view)
- Returns the root of the menu-tree with node
-
-
- |#
-
-
- (defconstant 2pi (* 2 pi) "Radians in a circle")
-
- (defconstant full-circle 360 "Degrees in a circle")
- (defconstant rads-to-degrees (/ full-circle 2pi))
-
- ;; default values for the marking menus
- (defconstant hole-size 10)
- (defconstant pop-menu-height 2)
- (defconstant pop-menu-diameter 170)
- (defconstant jitter-tol 5)
- (defconstant wait (round internal-time-units-per-second 12) "seconds to wait before popping up the menu")
-
- (defvar *arrow*) ; a large filled, right-pointing arrow
-
- ;; structure for storing points that describe the position of menu-item
- ;; rect-top-left coordinates of the rectangle enclosing the text of the
- ;; rect-bot-right title with white space
-
- ;; text-width half the width of the text string
-
- ;; text-top-left coordinates of the rectangle enclosing
- ;; text-bottom-right only the text
-
- ;; text-center: the center of the text string
- ;; slice-point: the point on the outer-most circle that
- ;; defines the start of the wedge
-
- (defstruct (item (:type list))
- (rect-top-left #@(0 0))
- (rect-bot-right #@(0 0))
- (text-center #@(0 0))
- (text-width 0)
- (text-start #@(0 0))
- (text-top-left #@(0 0))
- (text-bot-right #@(0 0))
- slice-point
- title)
-
- ;; a class of menu-items which corresponds to the divider in a pull-down menu
- (defclass empty-menu-item (menu-item)
- ()
- (:default-initargs
- :menu-item-title "-"
- :disabled t))
-
- (defclass marking-menu (menu)
- ((menu-diameter :initarg :menu-diameter)
- (auto-size :initarg :auto-size)
- (menu-height :initarg :offset)
- (menu-hole :initarg :hole-size)
- (menu-start-tol :initarg :menu-start-tol)
- (menu-font :initarg :menu-font)
- (on-axis :initarg :on-axis)
- (pop-up-time :initarg :pop-up-time)
- (menu-double-click-action-function :initarg :menu-double-click-action)
- (menu-actzone :initarg :menu-actzone)
- (menu-floating :initarg :menu-floating)
- (menu-opaque :initarg :menu-opaque)
- (turn :initarg :turn)
- (hide :initarg :hide)
- (in-position :initarg :in-position)
- (pop-width :initarg :pop-width)
- (viewer :initform nil) ; containing view
- (menu-title-rect :initform nil) ; list of topLeft bottomRight coordinates of the
- ; rectangles corresponding to the titles
- (menu-radius :initarg nil) ; radius of the marking menu
- (menu-center :initform nil) ; current center of the marking menu
- (menu-rect) ; rectangle enclosing the menu centered at #@(0 0)
- ; actual rectangle is offset by menu-center
- (saved-bit-map :initform nil) ; saved bit map of the screen obscured by the menu
- ; corresponding to the actual rectangle
- (arrow-size :initform #@(15 15)) ; dimensions of the arrow indicating a submenu
- (sized :initform nil) ; has the menu been resized?
- (real-corners :initform nil) ; corners of enclosing rectangle
- (arrow-indent :initform nil)) ; arrow starts indented arrow-indent from bottom-right
- (:default-initargs
- :menu-diameter pop-menu-diameter
- :auto-size t
- :offset pop-menu-height
- :menu-start-tol jitter-tol
- :menu-font '("Chicago" 12 :SRCOR :PLAIN)
- :hole-size hole-size
- :on-axis nil
- :pop-up-time wait
- :menu-actzone nil
- :menu-floating t
- :menu-opaque nil
- :hide t
- :turn t
- :in-position t
- :pop-width nil))
-
- (defclass marking-menu-view (marking-menu view)
- ())
-
- (defclass marking-menu-window (marking-menu-view window)
- ())
-
- (defclass marking-menu-table (sequence-dialog-item marking-menu-view)
- ())
-
- (defmethod page-forward ((self table-dialog-item))
- (let* ((first-cell (point-h (scroll-position self)))
- (ncells (point-h (table-dimensions self)))
- (visible-dimensions (point-h (visible-dimensions self)))
- (last-cell (min (1- (+ first-cell visible-dimensions))
- (- ncells visible-dimensions))))
- (when (< last-cell ncells)
- (scroll-to-cell self last-cell)
- (< (+ last-cell visible-dimensions) ncells))))
-
- (defmethod page-back ((self table-dialog-item))
- (let* ((first-cell (point-h (scroll-position self)))
- (visible-dimensions (point-h (visible-dimensions self)))
- new-cell)
- (setq new-cell (max 0 (1+ (- first-cell visible-dimensions))))
- (scroll-to-cell self new-cell)
- (not (zerop new-cell))))
-
- (defmethod page-down ((self table-dialog-item))
- (let* ((first-cell (point-v (scroll-position self)))
- (ncells (point-v (table-dimensions self)))
- (visible-dimensions (point-v (visible-dimensions self)))
- (last-cell (min (1- (+ first-cell visible-dimensions))
- (- ncells visible-dimensions))))
- (when (< last-cell ncells)
- (scroll-to-cell self (make-point 1 last-cell))
- (< (+ last-cell visible-dimensions) ncells))))
-
- (defmethod page-up ((self table-dialog-item))
- (let* ((first-cell (point-v (scroll-position self)))
- (visible-dimensions (point-v (visible-dimensions self)))
- new-cell)
- (setq new-cell (max 0 (1+ (- first-cell visible-dimensions))))
- (scroll-to-cell self (make-point 1 new-cell))
- (not (zerop new-cell))))
-
- (defmethod initialize-instance :after ((view marking-menu-view) &rest init-args)
- (declare (ignore init-args))
- (with-slots (menu-actzone pop-width menu-hole) view
- (unless (numberp pop-width)
- (setq pop-width (+ menu-hole menu-hole)))
- (when menu-actzone
- (let (topLeft bottomRight)
- (if (listp menu-actzone)
- (setq topLeft (first menu-actzone)
- bottomRight (second menu-actzone))
- (setq topLeft #@(0 0)
- bottomRight menu-actzone))
- (setq menu-actzone (make-record :rect :topLeft topLeft :bottomRight bottomRight))))))
-
- (defmethod remove-menu-view ((menu marking-menu))
- (when (slot-boundp menu 'menu-rect)
- (with-slots (menu-rect) menu
- (without-interrupts
- (when menu-rect
- (with-slots (saved-bit-map menu-actzone) menu
- (when (zone-pointerp menu-rect)
- (dispose-record menu-rect :rect))
- (when (zone-pointerp menu-actzone)
- (dispose-record menu-actzone :rect))
- (setq menu-actzone nil)
- (safe-kill-picture saved-bit-map)
- (slot-makunbound menu 'menu-rect))))
- (dolist (menu-item (menu-items menu))
- (when (is-menu menu-item)
- (remove-menu-view menu-item))))))
-
- (defmethod remove-view-from-window ((menu marking-menu))
- ;; remove items from the heap associated with a marking-menu
- (call-next-method)
- (remove-menu-view menu))
-
- (defmethod ccl:add-menu-items ((menu marking-menu) &rest menu-items)
- (apply #'call-next-method menu menu-items)
- (mapc #'(lambda (item)
- (when (is-menu item)
- (resize-menu item)))
- menu-items)
- (resize-menu menu))
-
- (defmethod ccl:remove-menu-items ((menu marking-menu) &rest menu-items)
- (apply #'call-next-method menu menu-items)
- (mapc #'(lambda (item)
- (when (is-menu item)
- (remove-menu-view item)))
- menu-items)
- (resize-menu menu))
-
- (defmethod set-menu-item-title (menu-item title)
- (apply #'call-next-method menu-item title)
- (let ((menu (menu-owner menu-item)))
- (resize-menu menu)))
-
- (defmethod set-menu-font ((menu marking-menu-view) font-spec)
- (setf (slot-value menu 'menu-font) (append font-spec '(:plain)))
- (set-arrow-size menu :force t)
- (resize-menu menu))
-
- (defmethod menu-root ((menu marking-menu-view))
- (let (next-menu)
- (when menu
- (loop
- while (setq next-menu (menu-owner menu))
- finally (return menu)
- do (setq menu next-menu)))))
-
- ;; routines for creating, drawing, deleting arrows
- (defun get-arrow ()
- ;; Create a filled right pointing arrow for a line-height of 256
- (unless (and (boundp '*arrow*) (handlep *arrow*))
- (with-wmgr-view
- (let* ((unit 32)
- (floor-unit (make-point (+ unit 64) 32))
- (floor-three-unit (* 3 unit))
- my-poly
- (wptr (wptr *wmgr-view*))
- pict)
- (rlet ((r :rect :topLeft #@(0 0) :bottomRight #@(255 255)))
- (with-clip-rect r
- (with-port wptr
- (setq my-poly (#_OpenPoly)))
- (#_MoveTo :long floor-unit)
- (#_Line 0 (* 6 unit))
- (#_Line floor-three-unit (- floor-three-unit))
- (#_LineTo :long floor-unit)
- (with-port wptr
- (#_ClosePoly))
- (setq pict (#_OpenPicture :ptr r))
- (#_PaintPoly :ptr my-poly)
- (#_closePicture)
- (#_KillPoly :ptr my-poly)
- (setq *arrow* pict)))))))
-
- (defmethod set-arrow-size ((menu marking-menu) &key force)
- (get-arrow)
- (unless (or force (slot-boundp menu 'arrow-size))
- (setf (slot-value menu 'arrow-size)
- (compute-arrow-size menu))))
-
- (defmethod compute-arrow-size ((menu marking-menu))
- (with-slots (menu-font) menu
- (let* ((top-left (href *arrow* :picture.picframe.topleft))
- (bottom-right (href *arrow* :picture.picframe.BottomRight))
- (size (subtract-points bottom-right top-left))
- (line-height (font-line-height menu-font))
- (factor (/ line-height 256)))
- (make-point (round (1+ (* factor (point-h size))))
- (round (1+ (* factor (point-v size))))))))
-
- (defun delete-arrow ()
- (when (and (boundp '*arrow*) (handlep *arrow*))
- (kill-picture *arrow*))
- (makunbound '*arrow*))
-
- (defun draw-arrow (new-size position)
- ;; draws the arrow with at the position, at size new-size
- (rlet ((r :rect :topLeft position
- :bottomRight (add-points position new-size)))
- (#_drawPicture :ptr *arrow* :ptr r)))
-
- (defmethod get-arrow-size ((menu marking-menu))
- )
-
- (defmethod containing-view ((menu marking-menu))
- ;; find the view containing the marking-menu
- (let ((viewer (when (slot-exists-p menu 'viewer)
- (slot-value menu 'viewer))))
- (if viewer viewer menu)))
-
- (defmethod containing-view ((ccl::menu-element menu-item))
- ;; find the view containing the marking-menu
- (let* ((owner (menu-item-owner ccl::menu-element)))
- (containing-view owner)))
-
- (defun point-box (x)
- "construct the point #@(x x)"
- (make-point x x))
-
- (defmethod init-menu-box ((menu marking-menu))
- ;; Fills in the menu slots after the first mouse click in the marking view
- (with-slots (saved-bit-map menu-center sized) menu
- (unless sized
- (resize-menu menu))
- (safe-kill-picture saved-bit-map)
- (setq menu-center nil)))
-
- (defmethod check-menu-box ((menu marking-menu) &optional flag)
- (unless (slot-boundp menu 'menu-rect)
- (init-menu-box menu)
- (when flag
- (print-db menu))))
-
- (defmethod resize-menu ((menu marking-menu))
- ;; For auto-size marking-menus
- ;; Calculates the menu-diameter and menu-radius of a marking-menu,
- ;; allowing enough whitespace
- ;; Ensures that the menu-item titles fit inside the circle associated
- ;; with the menu-rect (radius = (1- (abs (point-h top-left-corder)))
- (let* ((n-items (length (menu-items menu)))
- (do-size (slot-value menu 'auto-size))
- menu-rect)
- (set-arrow-size menu)
- (setf (slot-value menu 'menu-radius)
- (truncate (slot-value menu 'menu-diameter) 2))
- (if (> n-items 0)
- (with-slots (menu-diameter menu-hole) menu
- (with-slots (menu-font sized arrow-size arrow-indent) menu
- (multiple-value-bind (ascent descent) (font-info menu-font)
- (let* ((border descent)
- (theta/2 (/ pi n-items))
- width-list)
- (when (menu-owner menu) (menu-item-enable menu))
- (when do-size
- (setq menu-diameter (ash menu-hole 3))
- (setf (slot-value menu 'menu-radius) (ash menu-diameter -1)))
- (setq arrow-indent (make-point (+ border (point-h arrow-size))
- (+ border descent ascent))
- width-list (menu-calc-widths menu :full t)
- sized t)
- (when do-size
- (menu-calc-rect menu ascent descent border width-list theta/2))
- (menu-calc-outer menu ascent descent border width-list theta/2)
- (init-menu-box menu)))))
- (with-slots (saved-bit-map menu-center) menu
- ;; no items in the menu, disable the menu if it is submenu
- (when (menu-owner menu) (menu-item-disable menu))
- (when (and (slot-boundp menu 'menu-rect)
- (setq menu-rect (slot-value menu 'menu-rect))
- (zone-pointerp menu-rect))
- (dispose-record menu-rect :rect)
- (slot-makunbound menu 'menu-rect))
- (safe-kill-picture saved-bit-map)
- (setq menu-center nil)))))
-
- (defmethod menu-calc-widths ((menu marking-menu) &key full)
- ;;Returns a list with the half widths of the menu-item-titles including white space
- (with-slots (menu-font arrow-size) menu
- (let (style width half-width string font-spec width-list)
- (dolist (menu-item (menu-items menu))
- (setq string (format nil "~a" (menu-item-title menu-item)))
- (unless (equal string "-")
- (when (and full (slot-exists-p menu-item 'check-mark-char))
- (setq string (format nil "~a ~a" (slot-value menu-item 'check-mark-char) string))))
- (setq style (menu-item-style menu-item)
- font-spec (if style (append menu-font (list (menu-item-style menu-item)))
- menu-font))
- (with-font-spec font-spec
- (with-returned-pstrs ((text-buff string))
- (setq width (#_TextWidth :ptr text-buff :integer 1 :integer (length string)))
- (when (is-menu menu-item)
- (incf width (point-h arrow-size)))
- (setq half-width (ash (1+ width) -1))))
- (push half-width width-list))
- (setq width-list (nreverse width-list)))))
-
- (defun init-text-rect (text-box ascent descent border half-width)
- (rset text-box :rect.topLeft (make-point (- half-width) (- ascent)))
- (rset text-box :rect.bottomRight
- (make-point (+ border half-width)
- (+ border descent)))
- (#_InsetRect :ptr text-box :long (point-box (- border))))
-
- (defun on-axis (menu &optional (flag t))
- (if (null menu)
- flag
- (let ((next (when (and menu (slot-exists-p menu 'ccl::owner))
- (slot-value menu 'ccl::owner))))
- (cond ((null next)
- (if flag (not (slot-value menu 'on-axis))
- (slot-value menu 'on-axis)))
- ((slot-value menu 'turn)
- (if next
- (on-axis next (not flag))
- (if flag (not (slot-value menu 'on-axis))
- (slot-value menu 'on-axis))))
- (t flag)))))
-
- (defmethod menu-calc-rect ((menu marking-menu) ascent descent border width-list theta/2)
- ;; adjusts circle with menu-radius so that the boxes including the menu-item-titles
- ;; and white space satisfy the following conditions:
- ;; 1. The center of the base line of the text lies along a circle of radius menu-radius.
- ;; 2. The right and left halves of the text box cast a shadow of at most 180/#menu-items
- ;; with a point light source at (0,0)
- ;; 3. Each text box has enough whitespace between it and the central circle.
- ;; The text box lies outside a square centered at the origin with sides 2*menu-hole
- (with-slots (menu-radius menu-diameter menu-hole) menu
- (let* (angle
- (start-radius menu-radius)
- (min-move 1)
- (hole-space (ash menu-hole 0))
- x
- y
- text-center
- old-center
- (turn-before (on-axis menu))
- ;(half-degrees (* theta/2 rads-to-degrees))
- ;mid-slice
- offset)
- (rlet ((r :rect :topLeft (point-box (- menu-radius))
- :bottomRight (point-box menu-radius))
- (text-box :rect)
- (hole-box :rect :topLeft (point-box (- hole-space))
- :bottomRight (point-box hole-space))
- (u-rect :rect :topLeft #@(0 0) :bottomRight #@(0 0)))
- (setq angle (if turn-before (- theta/2)
- 0))
- (dolist (half-width width-list)
- (incf angle theta/2)
- (init-text-rect text-box ascent descent border half-width)
- (setq old-center #@(0 0))
- (loop
- do (progn (setq x (least-integer (* (sin angle) menu-radius))
- y (least-integer (* (cos angle) menu-radius))
- text-center (make-point x (- y))
- offset (subtract-points text-center old-center))
- (#_offsetRect :ptr text-box :long offset)
- (intersect-rect hole-box text-box u-rect)
- (unless (empty-rect-p u-rect)
- (setq menu-radius (increase-radius r menu-radius min-move))))
-
- until (box-rad text-box angle theta/2)
- finally (return t)
-
- do (setq menu-radius (increase-radius r menu-radius min-move)
- old-center text-center))
- (incf angle theta/2))
- (setq menu-diameter (ash menu-radius 1))
- (= menu-radius start-radius)))))
-
- (defun least-integer (x)
- (if (minusp x) (floor x)
- (ceiling x)))
-
- (defun rads-to-degrees (rads)
- (* rads rads-to-degrees))
-
- (defmethod menu-calc-outer ((menu marking-menu) ascent descent border width-list theta/2)
- ;; compute the smallest square surrounding the text-boxes in the menu,
- ;; centered at #@(0 0)
- (unless (and (slot-boundp menu 'menu-rect)
- (zone-pointerp (slot-value menu 'menu-rect)))
- (setf (slot-value menu 'menu-rect) (make-record :rect)))
- (with-slots (menu-radius menu-rect menu-height real-corners) menu
- (rlet ((u-rect :rect :topLeft #@(0 0) :bottomRight #@(0 0))
- (text-box :rect))
- (let ((angle (if (on-axis menu) (- theta/2)
- 0))
- real-angle
- x
- y
- text-center
- real-radius)
- (dolist (half-width width-list)
- (incf angle theta/2)
- (setq real-angle angle)
- (setq x (truncate (* (sin real-angle) menu-radius))
- y (truncate (* (cos real-angle) menu-radius))
- text-center (make-point x (- y)))
- (rset text-box :rect.topLeft (make-point (- half-width) (- 0 ascent)))
- (rset text-box :rect.bottomRight (make-point (+ border half-width)
- (+ border descent)))
- (#_insetRect :ptr text-box :long (point-box (- border)))
- (#_offsetRect :ptr text-box :long text-center)
- (union-rect u-rect text-box u-rect)
- (incf angle theta/2))
- (make-square-rect u-rect) ; enclose the title rectangles in a square
- (setq real-corners (list (rref u-rect :rect.TopLeft) (rref u-rect :rect.bottomRight)))
- (setq real-radius (point-h (rref u-rect :rect.bottomRight)))
- (rset menu-rect
- :rect.topLeft (point-box (1- (- real-radius))))
- (rset menu-rect
- :rect.bottomRight (point-box (+ (* 2 menu-height) real-radius)))))))
-
- (defun make-square-rect (r)
- "Adjust the rectangle r centered at #@(0 0)
- to the smallest square such that an inscribed circle
- encloses the original rectangle."
- (multiple-value-bind (p1 p2 p3 p4)
- (rect-corners r)
- (let ((max 0)
- dim-h dim-v)
- (dolist (p (list p1 p2 p3 p4))
- (setq dim-h (point-h p)
- dim-v (point-v p))
- (setq max (max max (+ (* dim-h dim-h) (* dim-v dim-v)))))
- (setq max (isqrt max))
- (rset r :rect.topLeft (point-box (- max)))
- (rset r :rect.bottomRight (point-box max)))))
-
- (defun rect-corners (r)
- "Return the four points corresponding to the corners of the rectangle r
- in the clockwise direction starting with the top-left"
- (let* ((top-left (rref r :rect.topLeft))
- (bottom-right (rref r :rect.bottomRight))
- (top (point-v top-left))
- (bottom (point-v bottom-right))
- (left (point-h top-left))
- (right (point-h bottom-right)))
- (values top-left bottom-right (make-point right top) (make-point left bottom))))
-
-
- (defun included-angle (angle)
- (if (minusp angle)
- (included-angle (- angle))
- (if (>= angle pi)
- (included-angle (- angle 2pi))
- angle)))
-
- (defun included-arc (degrees)
- (if (minusp degrees)
- (included-arc (- degrees))
- (if (>= degrees 180)
- (included-arc (- degrees 360))
- degrees)))
-
- (defun point-angle (p1 p2)
- "The angle between the directed line p1 and p2 relative to the vertical line"
- (let* ((diff (subtract-points p2 p1))
- (diff-h (point-h diff))
- (diff-v (point-v diff))
- angle)
- (setq angle (atan diff-h (- diff-v)))
- (when (minusp angle)
- (incf angle 2pi))
- angle))
-
- (defun box-angle (hole-box text-box mid-slice half-degrees)
- "Compute the angle of the shadow cast by the rectangle text-box
- and the point light source at the center of the square r
- return t when the angle is <= degrees"
- (let (angle)
- (multiple-value-bind (top-left top-right bottom-right bottom-left)
- (rect-corners text-box)
- (dolist (point (list top-left top-right bottom-right bottom-left))
- (setq angle (point-to-angle hole-box point))
- (when (> (included-arc (- angle mid-slice)) half-degrees)
- (return-from box-angle nil)))
- t)))
-
- (defun box-rad (text-box angle theta/2)
- "Compute the angle of the shadow cast by the rectangle text-box
- and the point light source at the center of the square r
- return t when the angle is <= degrees"
- (let (new-angle)
- (multiple-value-bind (top-left top-right bottom-right bottom-left)
- (rect-corners text-box)
- (dolist (point (list top-left top-right bottom-right bottom-left))
- (setq new-angle (point-angle #@(0 0) point))
- (when (>= (included-angle (- angle new-angle)) theta/2)
- (return-from box-rad nil)))
- t)))
-
- (defun increase-radius (r radius amount)
- (when (and amount (numberp amount) (> amount 0))
- (incf radius amount)
- (inset-rect r (point-box (- amount))))
- radius)
-
- (defun get-max-dim (r)
- (let* ((top-left (rref r :rect.topLeft))
- (bottom-right (rref r :rect.bottomRight))
- (dim (subtract-points bottom-right top-left)))
- (max (point-h dim) (point-v dim))))
-
-
- (defun draw-hole (menu r)
- (with-slots (menu-center menu-hole menu-floating) menu
- (let ((hole-box (point-box menu-hole)))
- (rset r :rect.topLeft (subtract-points menu-center hole-box))
- (rset r :rect.bottomRight (add-points menu-center hole-box))
- (when menu-floating
- (#_offsetRect :ptr r :long #@(2 2))
- (#_fillOval :ptr r :ptr *black-pattern*)
- (#_offsetRect :ptr r :long #@(-2 -2)))
- (#_EraseOval :ptr r)
- (#_frameOval :ptr r))))
-
- (defun get-radius-box (menu)
- (with-slots (menu-rect) menu
- (subtract-points #@(-1 -1) (rref menu-rect :rect.topLeft))))
-
- (defmethod display-marking-menu ((menu marking-menu))
- ;; displays the circular marking menu
- (with-slots (on-axis menu-rect menu-center menu-diameter menu-floating menu-opaque
- menu-font menu-radius menu-height menu-title-rect
- arrow-size real-corners) menu
- (let* ((center (make-point (add-points
- (add-points (point-box (- menu-radius))
- (point-box (truncate (/ menu-diameter 2))))
- menu-center)))
- (menu-items (menu-items menu))
- (size (length menu-items))
- (radius-box (get-radius-box menu))
- (real-radius (point-h radius-box))
- (theta/2 (/ pi size))
- (theta (* 2 theta/2))
- (angle 0)
- (center-h (point-h center))
- (center-v (point-v center))
- font-spec
- real-angle
- ascent
- descent
- border
- style
- item-spec)
- (with-pen-state (:pnmode #$patCopy :pnPat *black-pattern*)
- (rlet ((r :rect :topLeft (subtract-points center radius-box)
- :bottomRight (add-points (add-points center radius-box) #@(1 1)))
- (clip-rect1 :rect))
-
- (if menu-floating
- (progn (rset clip-rect1 :rect.topLeft (first real-corners))
- (rset clip-rect1 :rect.bottomRight (second real-corners)))
- (copy-record menu-rect :rect clip-rect1))
- (#_offsetRect :ptr clip-rect1 :long center)
- (with-clip-rect clip-rect1
- (cond (menu-opaque
- (without-interrupts
- (with-pen-state (:pnMode #$patBic :pnPat *light-gray-pattern*)
- (#_OffsetRect :ptr r :long (point-box menu-height))
- (if menu-floating
- (#_PaintRect :ptr r)
- (#_PaintOval :ptr r)))
- (#_OffsetRect :ptr r :long (point-box (- menu-height)))
- (unless menu-floating
- (#_frameOval :ptr r))))
- (menu-floating t)
- (t (without-interrupts
- (#_OffsetRect :ptr r :long (point-box menu-height))
- (#_PaintOval :ptr r)
- (#_OffsetRect :ptr r :long (point-box (- menu-height)))
- (#_EraseOval :ptr r)
- (#_FrameOval :ptr r))))
- (unless on-axis (setq angle (decf angle theta/2)))
- (setq menu-title-rect nil)
- (with-font-spec menu-font
- (multiple-value-setq (ascent descent) (font-info))
- (setq border descent)
- (dolist (item menu-items)
- (setq style (menu-item-style item)
- font-spec (if style (append menu-font (list (menu-item-style item)))
- menu-font))
- (with-font-spec font-spec
- (setq real-angle angle
- item-spec
- (calc-menu-item r menu-radius real-radius center-h center-v theta/2
- real-angle ascent descent border
- item menu-floating arrow-size))
- (draw-title menu item item-spec t)
- (push item-spec menu-title-rect))
- (incf angle theta))
- (setq menu-title-rect (nreverse menu-title-rect))
- (draw-hole menu r))))))))
-
- (defun calc-menu-item (r menu-radius real-radius center-h center-v theta/2
- real-angle ascent descent border
- menu-item menu-floating arrow-size)
- (let (width x y string menu-item-enabled new-point
- top-left bottom-right text-center slice-point
- text-top text-bot
- check-mark
- arrow-width
- half-width)
- (unless menu-floating
- (setq x (truncate (* (sin real-angle) real-radius))
- y (truncate (* (cos real-angle) real-radius))
- new-point (make-point (+ x center-h) (- center-v y))
- slice-point new-point))
- (incf real-angle theta/2)
- (setq x (truncate (* (sin real-angle) menu-radius))
- y (truncate (* (cos real-angle) menu-radius))
- string (format nil "~a" (menu-item-title menu-item))
- menu-item-enabled (menu-item-enabled-p menu-item))
- (when (and menu-floating (equal string "-"))
- (return-from calc-menu-item (make-item :slice-point slice-point)))
- (unless (equal string "-")
- (when (slot-exists-p menu-item 'check-mark-char)
- (when (setq check-mark (menu-item-check-mark menu-item))
- (setq string (format nil "~a ~a" check-mark string)))))
- (with-returned-pstrs ((text-buff string))
- (setq width (#_TextWidth :ptr text-buff :integer 1 :integer (length string))
- text-center (make-point (+ x center-h) (- center-v y)))
- (when (is-menu menu-item)
- (setq arrow-width (point-h arrow-size))
- (incf width arrow-width))
- (setq half-width (ash (1+ width) -1))
- (decf x half-width)
- (setq new-point (make-point (+ x center-h) (- center-v y)))
- (rset r :rect.topLeft (make-point (- half-width) (- ascent)))
- (rset r :rect.bottomRight (make-point half-width descent))
- (#_insetRect :ptr r :long (point-box (- border)))
- (#_offsetRect :ptr r :long text-center)
- (setq top-left (rref r :rect.topLeft)
- bottom-right (rref r :rect.bottomRight)
- text-top (subtract-points text-center (make-point half-width ascent))
- text-bot (add-points text-center (make-point half-width descent)))
- ; handle disabled items
- (unless menu-item-enabled
- (rset r :rect.topLeft text-top)
- (rset r :rect.bottomRight text-bot)
- ( #_InsetRect :ptr r :long #@(0 -1))
- (with-pen-state (:pnPat *gray-pattern* :pnMode #$PatBic)
- (#_PaintRect :ptr r)))
- (make-item :rect-top-left top-left
- :rect-bot-right bottom-right
- :text-center text-center
- :text-width half-width
- :text-start new-point
- :slice-point slice-point
- :text-top-left text-top
- :text-bot-right text-bot
- :title string))))
-
-
- (defun draw-title (menu menu-item item-spec &optional in-place hilite)
- (with-slots (menu-hole menu-opaque menu-floating arrow-size arrow-indent) menu
- (let* ((menu-item-enabled (menu-item-enabled-p menu-item))
- (center (slot-value menu 'menu-center))
- (top-left (item-rect-top-left item-spec))
- (bottom-right (item-rect-bot-right item-spec))
- (text-top-left (item-text-top-left item-spec))
- (text-bot-right (item-text-bot-right item-spec))
- (half-width (item-text-width item-spec))
- (slice-point (item-slice-point item-spec))
- (menu-item-title (item-title item-spec))
- (in-position (or (not (slot-value menu 'menu-floating))
- (when (slot-exists-p menu 'in-position)
- (slot-value menu 'in-position))))
- (text-center (item-text-center item-spec))
- (new-center (if (or in-place in-position)
- text-center
- (subtract-points center (make-point (+ half-width menu-hole 5) 0))))
- (new-point (subtract-points new-center half-width)))
- (when slice-point
- (#_MoveTo :long center)
- (#_LineTo :long slice-point))
- (when (null menu-item-title)
- (return-from draw-title t))
- (rlet ((r :rect :topLeft top-left :bottomRight bottom-right))
- (with-pen-state (:pnPat *black-pattern*
- :pnMode #$patCopy))
- (if (or in-position in-place)
- (when menu-floating
- (#_MoveTo :long center)
- (#_LineTo :long text-center)
- (#_offsetRect :ptr r :long #@(2 2))
- (#_paintRect :ptr r)
- (#_offsetRect :ptr r :long #@(-2 -2)))
- (#_offsetRect :ptr r :long (subtract-points new-center
- text-center)))
- (when (or menu-floating menu-opaque)
- (#_eraseRect :ptr r))
- (when menu-floating
- (#_frameRect :ptr r))
- (with-font-spec (slot-value menu 'menu-font)
- (with-returned-pstrs ((text-buff menu-item-title))
- (#_MoveTo :long new-point)
- (#_DrawText :ptr text-buff :integer 1 :integer (length menu-item-title))))
- (when (is-menu menu-item)
- (setq new-point (subtract-points (rref r :rect.bottomRight)
- arrow-indent))
- (draw-arrow arrow-size new-point)
- (unless (menu-items menu-item)
- (setq menu-item-enabled nil)
- (menu-item-disable menu-item)))
-
- (unless menu-item-enabled
- (rset r :rect.topLeft text-top-left)
- (rset r :rect.bottomRight text-bot-right)
- (#_InsetRect :ptr r :long #@(0 -1))
- (with-pen-state (:pnPat *gray-pattern* :pnMode #$PatBic)
- (#_PaintRect :ptr r)))
- (when hilite (#_invertRect :ptr r))))))
-
- (defmethod mouse-position ((menu marking-menu) &optional point)
- ;; convert current mouse position to global coordinates
- (let ((viewer (when (slot-boundp menu 'viewer)
- (slot-value menu 'viewer))))
- (unless viewer (setq viewer menu))
- (local-to-global viewer (if point point
- (view-mouse-position viewer)))))
-
- (defmethod selected-slice ((menu marking-menu) &key position)
- ;;Returns the menu-item number corresponding to the selection
- (with-slots (on-axis menu-center) menu
- (unless (in-the-hole menu)
- (let* ((mouse-loc (if position position (mouse-position menu)))
- (size (length (menu-items menu)))
- (diff (subtract-points mouse-loc menu-center))
- theta)
- (setq theta (atan (point-h diff) (- (point-v diff))))
- (when (minusp theta)
- (incf theta 2pi))
- (unless on-axis
- (incf theta (/ pi size)))
- (when (> theta 2pi)
- (decf theta 2pi))
- (mod (truncate theta (/ 2pi size)) size)))))
-
- (defun get-menu-item-rect (menu-title-rect item)
- (let ((value (nth item menu-title-rect)))
- (values (item-rect-top-left value) (item-rect-bot-right value))))
-
- (defmethod invert-item ((menu marking-menu) item)
- ;; inverts the pixels in the displayed marking menu corresponding to the item number
- (unless (null item)
- (with-slots (on-axis menu-center menu-floating menu-opaque menu-title-rect) menu
- (when (menu-item-enabled-p (nth item (menu-items menu)))
- (if (or menu-floating menu-opaque)
- (multiple-value-bind (top-left bottom-right)
- (get-menu-item-rect menu-title-rect item)
- (rlet ((r :rect
- :topLeft top-left
- :bottomRight bottom-right))
- (#_InvertRect :ptr r)))
- (let* ((size (length (menu-items menu)))
- (radius-box (get-radius-box menu))
- (slice-size (/ full-circle size))
- (start-angle (* item slice-size)))
- (rlet ((r :rect
- :topLeft (subtract-points menu-center radius-box)
- :bottomRight (add-points (add-points menu-center radius-box) #@(1 1))))
- (unless on-axis (decf start-angle (/ slice-size 2)))
- (when (minusp start-angle)
- (incf start-angle full-circle))
- (#_InvertArc :ptr r :signed-integer (truncate start-angle)
- :signed-integer (truncate slice-size)))))))))
-
- (defun in-the-hole (menu)
- ;; determines whether the current mouse position is in the center of the menu
- (with-slots (menu-center menu-hole) menu
- (let* ((diff (subtract-points (mouse-position menu) menu-center)))
- (< (+ (abs (point-h diff)) (abs (point-v diff))) menu-hole))))
-
- (defmethod mark-stroke ((menu marking-menu) start-point)
- ;; leaves a ink trail on the screen until the mouse button is released
- ;; or the mouse remains in roughly the same spot for a dwell time.
- (let ((prev-loc start-point)
- loc condition
- stroke)
- (with-pen-state (:pnMode #$patxor)
- (push start-point stroke)
- (setq loc prev-loc)
- (#_MoveTo :long start-point)
- (loop
- do (setq loc (mouse-position menu))
- until (and (mouse-still menu) (setq condition 'still))
- while (#_WaitMouseUp)
- finally (return t)
- do (unless (equal loc prev-loc)
- (#_LineTo :long prev-loc)
- (#_LineTo :long loc)
- (push loc stroke)
- (setq prev-loc loc)))
- (values stroke
- condition
- loc))))
-
- (defun erase-stroke (stroke)
- ;; erases the marks made by mark-stroke
- (when stroke
- (let (point last-point)
- (setq stroke (nreverse stroke))
- (with-pen-state (:pnMode #$patxor)
- (setq last-point (first stroke))
- (#_MoveTo :long last-point)
- (loop
- while stroke
- finally (return t)
- do (#_LineTo :long last-point)
- (setq point (pop stroke)
- last-point point)
- (#_LineTo :long point))))))
-
- (defvar *marking-menu-class* (make-instance 'marking-menu))
-
- (defmethod find-marking-menu ((menu marking-menu) where)
- ;; find the deepest marking menu containing the point within the view
- ;; associated with the menu
- (let ((sv (find-view-containing-point menu where)))
- (loop
- until (or (eq sv menu) (null sv)
- (and (member *marking-menu-class* (class-precedence-list (class-of sv)))
- (setq menu sv)))
- finally (return menu)
- do (setq sv (view-container sv)))))
-
- (defmethod menu-double-click-action ((menu marking-menu))
- (when (slot-boundp menu 'menu-double-click-action-function)
- (funcall (slot-value menu 'menu-double-click-action-function) menu)))
-
- (defmethod do-menu-item-action ((ccl::menu-element menu-item) &optional param)
- (declare (ignore param))
- (let ((menu-item-action (menu-item-action-function ccl::menu-element)))
- (when menu-item-action (funcall menu-item-action))))
-
- (defmethod do-menu-item-action ((menu-item window-menu-item) &optional param)
- (let ((menu-item-action (menu-item-action-function menu-item)))
- (when menu-item-action (funcall menu-item-action param))))
-
- (defmethod do-menu-item-action ((marking-menu marking-menu-view) &optional param)
- (declare (ignore marking-menu param))
- nil)
-
- (defmethod view-click-event-handler ((menu marking-menu) where)
- ;; handles mouse clicks in marking-menus associated with marking-menu-views
- ;; the most specifc subview with view-click-event-handlers overrides the
- ;; containing view with a marking menu associated with it.
- (call-next-method menu where)
- (marking-menu-track menu where))
-
- (defmethod view-click-event-handler ((marking-menu marking-menu-table) where)
- ;; handles mouse clicks in marking-menu-tables
- ;; since there are no subviews, this routine handles the clicks in
- ;; the content area (e.g. the cells and not in the thumbs/scrollbars)
- (let* ((sv marking-menu)
- (point (convert-coordinates where (view-container sv) sv))
- (the-cell (point-to-cell sv where)))
- (if (equal (slot-value sv 'ccl::selection-type) :single)
- (if the-cell
- (progn
- (mapc #'(lambda (u)
- (unless (eq u the-cell)
- (cell-deselect sv u)))
- (selected-cells sv))
- (unless (cell-selected-p sv the-cell)
- (cell-select sv the-cell))
- (marking-menu-track sv point)) ; invoke the marking menu
- (call-next-method marking-menu where))
- (call-next-method marking-menu where))))
-
- (defmacro with-saved-bit-map ((menu &key center)
- &rest body)
- `(with-Wmgr-view
- (with-slots (menu-center saved-bit-map) ,menu
- (unwind-protect
- (progn (check-menu-box ,menu)
- (setq menu-center ,center)
- (setq saved-bit-map (save-bit-map ,menu))
- ,@body)
- (restore-bit-map ,menu :kill t)))))
-
- (defmethod marking-menu-track ((menu marking-menu) where)
- (let* ((sv (find-marking-menu menu where))
- (menu-actzone (slot-value menu 'menu-actzone))
- (target-menu menu))
- (when (and (mouse-down-p)
- (eq menu sv)
- (or (null menu-actzone)
- (not (zone-pointerp menu-actzone))
- (point-in-rect-p menu-actzone where)))
- (with-focused-view menu
- (if (double-click-p)
- (menu-double-click-action menu)
- (let ((menu-items (menu-items menu)))
- (when menu-items
- (let* (stroke
- cond
- prev-item point
- (start-pos (local-to-global menu where)))
- (with-saved-bit-map (menu :center start-pos)
- (multiple-value-setq (stroke cond point)
- (mark-stroke menu start-pos))
- (erase-stroke stroke)
- (setq prev-item (selected-slice menu :position point))
- (when (equal cond 'still)
- (multiple-value-setq (prev-item target-menu)
- (track-and-hilite menu prev-item))
- (setq prev-item nil)))
- (with-port (wptr menu)
- (when (and prev-item (eq menu target-menu))
- (let ((menu-item (nth prev-item (menu-items menu))))
- (do-menu-item-action menu-item menu-item))))
- t))))))))
-
- (defun draw-radial-line (center point &optional flag)
- ;; draws a line from the center to the given point
- (#_MoveTo :long center)
- (#_LineTo :long center)
- (#_LineTo :long point)
- (when flag
- (format t "~&~s ~a -> ~a~%"
- flag (point-string center) (point-string point))))
-
- (defun get-centers (menu)
- (when menu
- (setq menu (slot-value menu 'ccl::owner))
- (let (centers)
- (loop
- while menu
- finally (return-from get-centers centers)
- do (push (list (slot-value menu 'menu-center)
- (slot-value menu 'menu-hole)
- menu) centers)
- (setq menu (menu-owner menu))))))
-
- (defun point-in-hole (menu-center menu-hole point)
- ;; determines whether the point is in the center of the menu
- (let* ((diff (subtract-points point menu-center)))
- (< (+ (abs (point-h diff)) (abs (point-v diff))) menu-hole)))
-
- (defun find-hole (centers point)
- (dolist (menu centers)
- (when (point-in-hole (first menu) (second menu) point)
- (return-from find-hole (third menu)))))
-
- (defun is-menu (menu-item)
- (and menu-item
- (slot-exists-p menu-item 'ccl::menu-id)))
-
- (defun erase-to-parent (menu)
- (let ((prev-menu (menu-owner menu)) prev-center)
- (with-slots (menu-center) menu
- (when prev-menu
- (setq prev-center (slot-value prev-menu 'menu-center))
- (draw-radial-line prev-center menu-center)
- (restore-bit-map menu)))))
-
- (defun outside-circle (center radius point)
- ;; determines whether the point is outside the circle of the specified radius
- (let* ((diff (subtract-points center point)))
- (> (+ (abs (point-h diff)) (abs (point-v diff))) radius)))
-
- (defmacro push-viewer (submenu view viewer)
- ;; ensure that the submenu inherits the attributes of the root menu
- `(progn
- (dolist (el '(menu-opaque menu-floating hide on-axis turn in-position))
- (when (slot-exists-p ,submenu el)
- (setf (slot-value ,submenu el)
- (slot-value ,view el))))
- (when (slot-value ,view 'turn)
- (setf (slot-value ,submenu 'on-axis)
- (not (slot-value ,view 'on-axis))))
- (setq ,viewer ,view)))
-
- (defmacro pop-viewer (viewer submenu)
- `(with-slots (saved-bit-map) ,submenu
- (setq ,viewer nil)
- (safe-kill-picture saved-bit-map)))
-
- (defmacro with-pushed-viewer ((submenu view viewer) &rest body)
- `(progn
- (push-viewer ,submenu ,view ,viewer)
- ,@body
- (pop-viewer ,viewer ,submenu)))
-
- (defun is-marking-menu (menu)
- (and (is-menu menu) (menu-items menu)))
-
- (defmethod track-and-hilite ((menu marking-menu) prev-item)
- ;; Hilites the various sections of the menu and tracks mouse movement
- ;; until the mouse botton is released.
- (with-slots (menu-center viewer pop-width) menu
- (let (last-point
- point
- item
- is-menu
- menu-item
- menu-item-center
- (view (if viewer viewer menu))
- target-menu
- mouse-still
- (centers (get-centers menu))
- parent-center)
- ;; bug: need to mention under MCL2.0f3
- menu-item-center parent-center
- (display-marking-menu menu)
- (when prev-item
- (invert-item menu prev-item))
- (setq menu-item (when prev-item (nth prev-item (menu-items menu)))
- is-menu (is-marking-menu menu-item))
- (with-pen-state (:pnMode #$patxor)
- (loop
- (setq target-menu menu)
- (unless (#_WaitMouseUp)
- (when last-point
- (draw-radial-line menu-center last-point)
- (when prev-item
- (invert-item menu prev-item)))
- (return t))
-
- (setq point (mouse-position menu)
- item (selected-slice menu :position point))
-
- (unless (equal last-point point)
- (when last-point
- (draw-radial-line menu-center last-point))
- (setq last-point point)
- (draw-radial-line menu-center last-point)
- (unless (equal item prev-item)
- (invert-item menu prev-item)
- (invert-item menu item)
- (setq prev-item item)
- (when prev-item
- (setq menu-item (nth prev-item (menu-items menu))
- is-menu (is-marking-menu menu-item)))))
-
- (setq mouse-still (mouse-still view))
- (when mouse-still
- (if (setq target-menu (find-hole centers point))
- (unless (eq target-menu menu)
- (draw-radial-line menu-center last-point)
- (return))
- (when (and is-menu
- (outside-circle menu-center pop-width point))
- (setq menu-item (nth prev-item (menu-items menu)))
- (check-menu-box menu)
- (setq parent-center menu-center)
- (with-slots (viewer hide) menu-item
- (with-pushed-viewer (menu-item view viewer)
- (if hide
- (show-slice menu prev-item last-point) ; display the new menu and draw line
- (draw-radial-line menu-center last-point)) ; draw the line
- (multiple-value-setq (target-menu point)
- (hier-menu-track menu-item last-point menu-center))
- (setq menu-item-center (slot-value menu-item 'menu-center))))
- (setq prev-item nil)
- (unless (eq target-menu menu)
- (return))
- (setq last-point nil)
- (restore-bit-map menu)
- (display-marking-menu menu)))))
- (erase-to-parent menu))
- (when (eq target-menu menu)
- (when prev-item
- (let ((menu-item (nth prev-item (menu-items menu))))
- (do-menu-item-action menu-item menu-item)))
- (setq target-menu nil))
- (values prev-item
- target-menu
- point))))
-
- (defun show-slice (menu item last-point)
- (if (slot-value menu 'menu-floating)
- (do-parent menu item last-point)
- (with-slots (menu-rect menu-center menu-height on-axis menu-opaque) menu
- (let* ((size (length (menu-items menu)))
- (slice-size (/ full-circle size))
- (start-angle (* item slice-size))
- bottom-right)
- (unless on-axis (decf start-angle (/ slice-size 2)))
- (when (minusp start-angle)
- (decf start-angle 360))
- (rlet ((r :rect))
- (draw-radial-line menu-center last-point)
- (restore-bit-map menu)
- (copy-record menu-rect :rect r)
- (#_offsetRect :ptr r :long menu-center)
- (setq bottom-right (rref r :rect.bottomRight))
- (rset r :rect.bottomRight (subtract-points bottom-right
- (point-box menu-height)))
- (if menu-opaque
- (with-pen-state (:pnMode #$patBic
- :pnPat *light-gray-pattern*)
- (#_PaintArc :ptr r :signed-integer start-angle :signed-integer slice-size))
- (#_eraseArc :ptr r :signed-integer start-angle :signed-integer slice-size))
- (#_FrameArc :ptr r :signed-integer start-angle :signed-integer slice-size)
- (write-title menu item)
- (draw-hole menu r))))))
-
- (defun write-title (menu n)
- (with-slots (menu-title-rect menu-floating menu-center) menu
- (let* ((menu-item (nth n (menu-items menu)))
- (item-spec (nth n menu-title-rect))
- circle-pos)
- (draw-title menu menu-item item-spec nil t)
- (unless menu-floating
- (setq circle-pos (nth (1+ n) menu-title-rect))
- (unless circle-pos (setq circle-pos (nth 0 menu-title-rect)))
- (setq circle-pos (item-slice-point circle-pos))
- (when circle-pos
- (#_MoveTo :long menu-center)
- (#_LineTo :long circle-pos))))))
-
- (defun do-parent (menu n point)
- (with-pen-state (:pnMode #$patxor :pnPat *black-pattern*)
- (with-slots (menu-center) menu
- (rlet ((r :rect))
- (draw-radial-line menu-center point)
- (restore-bit-map menu :kill nil)
- (write-title menu n)
- (draw-hole menu r)))))
-
- (defmethod hier-menu-track ((menu marking-menu) where target-menu-center)
- (when (mouse-down-p)
- (let ((menu-items (menu-items menu)))
- (when menu-items
- (let (prev-item
- (start-pos where)
- (point where)
- (target-menu menu))
- (with-saved-bit-map (menu :center start-pos)
- (draw-radial-line start-pos target-menu-center)
- (setq prev-item (selected-slice menu :position point))
- (loop
- do (multiple-value-setq (prev-item target-menu point)
- (track-and-hilite menu prev-item))
- while (eq target-menu menu)
- finally (progn (when target-menu
- (setq prev-item nil))
- (return))
- do (progn (restore-bit-map menu :kill nil)
- (display-marking-menu menu)))
- )
- (values target-menu point))))))
-
- (defmethod mouse-still ((menu marking-menu))
- ;; determines whether the mouse is relatively still -
- ;; the mouse button is down and the manhattan distance of
- ;; the current position of the mouse is at most menu-start-tol pixels
- ;; from the mouse-position when the method is run.
- (with-slots (menu-start-tol pop-up-time) menu
- (let ((start (mouse-position menu))
- (t0 (get-internal-run-time))
- loc
- still)
- (loop
- until (or
- (and (> (- (get-internal-run-time) t0) pop-up-time)
- (setq still t))
- (not (#_WaitMouseUp)))
- do (setq loc (subtract-points (mouse-position menu)
- start))
- until (> (+ (abs (point-h loc)) (abs (point-v loc)))
- menu-start-tol)
- finally (return still)))))
-
- (defmethod save-bit-map ((menu marking-menu))
- ;; saves the bit map of the graph-port corresponding to the offset menu-rect
- ;; of the window containing the marking-menu
- (check-menu-box menu t)
- (with-slots (menu-rect menu-center saved-bit-map menu-floating real-corners) menu
- (rlet ((rect :rect))
- (safe-kill-picture saved-bit-map)
- (if menu-floating
- (progn (rset rect :rect.topLeft (first real-corners))
- (rset rect :rect.bottomRight (second real-corners)))
- (copy-record menu-rect :rect rect))
- (#_offsetRect :ptr rect :long menu-center)
- (setq saved-bit-map (save-screen-map (containing-view menu) rect)))))
-
- (defmethod restore-bit-map ((menu marking-menu) &key kill)
- ;; restores the bit map corresponding to the offset menu-rect
- (with-slots (menu-center menu-rect saved-bit-map menu-floating real-corners) menu
- (rlet ((rect :rect))
- (copy-record menu-rect :rect rect)
- (if menu-floating
- (progn (rset rect :rect.topLeft (first real-corners))
- (rset rect :rect.bottomRight (second real-corners)))
- (copy-record menu-rect :rect rect))
- (#_offsetRect :ptr rect :long menu-center)
- (restore-screen-map saved-bit-map rect)
- (when kill
- (safe-kill-picture saved-bit-map)))))
-
- (defun check-arrow ()
- (def-load-pointers init-arrow nil (get-arrow))
- (setq *save-exit-functions*
- (remove-if
- #'(lambda (item)
- (equal (function-name item) 'delete-arrow))
- *save-exit-functions*))
- (push #'delete-arrow *save-exit-functions*)
- t)
-
- (check-arrow)
- #|
- For complete examples see:
- marking-menu-demo.lisp
- hier-menu-demo.lisp
- |#
-